package entity

import (
	"strconv"
	"time"

	"gitee.com/chenhonghua/ginorigin/database"
	"gorm.io/gorm"
)

type Exercise struct {
	Id          int        `json:"id" gorm:"primary_key;auto_increment;column:id;comment:'题目ID'"`
	CreatedTime time.Time  `json:"created_time" gorm:"column:created_time;default:CURRENT_TIMESTAMP;comment:'创建时间'"`
	UpdatedTime time.Time  `json:"updated_time" gorm:"column:updated_time;default:CURRENT_TIMESTAMP;comment:'更新时间'"`
	State       state      `json:"state" gorm:"column:state;default:0;comment:'状态'"`
	Morphology  morphology `json:"morphology" gorm:"column:morphology;comment:'题目形态。如单选题、多选题、填空题、解答题等'"`
	ParentId    int        `json:"parent_id" gorm:"column:parent_id;comment:'父题目ID'"`
	Sequence    int        `json:"sequence" gorm:"column:sequence;comment:'顺序'"`
	Content     string     `json:"content" gorm:"column:content;comment:'内容'"`
	Analysis    string     `json:"analysis" gorm:"column:analysis;comment:'解析'"`
	Answer      string     `json:"answer" gorm:"column:answer;comment:'题目答案'"`

	Options []Option `json:"options" gorm:"foreignKey:ExerciseId;references:Id;comment:'选项'"`

	oldId int        `json:"-" gorm:"-"`
	Subs  []Exercise `json:"subs" gorm:"foreignKey:ParentId;references:Id;comment:'子题目'"`
}

func (exec *Exercise) TableName() string {
	return "exercise"
}

type Option struct {
	Id          int       `json:"id" gorm:"primary_key;auto_increment;column:id;comment:'选项ID'"`
	CreatedTime time.Time `json:"created_time" gorm:"column:created_time;default:CURRENT_TIMESTAMP;comment:'创建时间'"`
	ExerciseId  int       `json:"-" gorm:"column:exercise_id;comment:'题目ID'"`
	Tag         string    `json:"tag" gorm:"column:tag;comment:'选项标签'"`
	Content     string    `json:"content" gorm:"column:content;comment:'选项内容'"`
	IsAnswer    bool      `json:"is_answer" gorm:"column:is_answer;comment:'是否为正确答案'"`
}

func (opt *Option) TableName() string {
	return "option"
}

func (exec *Exercise) upgradeOption(tx *gorm.DB, options []Option) (*gorm.DB, error) {
	if e := tx.Model(&Option{}).Where("exercise_id in ?", []int{exec.oldId, exec.Id}).Delete(&Option{}).Error; e != nil { // 清空此题的选项关联关系
		return tx, e
	} else if len(options) == 0 {
		return tx, nil
	}
	var maxTag int = 0
	for i, opt := range options {
		if len(opt.Tag) > 0 {
			if v, e := strconv.Atoi(opt.Tag); e == nil && v > maxTag {
				maxTag = v
				continue
			}
		}
		options[i].Tag = ""
	}
	for _, option := range options {
		option.Id = 0
		option.ExerciseId = exec.Id
		if len(option.Tag) == 0 {
			
		}
		if e := tx.Model(&Option{}).Create(&option).Error; e != nil {
			return tx, e
		}
	}
	exec.Options = options
	return tx, nil
}

func (exec *Exercise) operate(tx *gorm.DB, isCreate bool) (e error) {
	if len(exec.Subs) > 0 {
		exec.Morphology = MorphologyAssembly
	}
	exec.oldId = exec.Id
	subs := exec.Subs
	exec.Subs = nil
	options := exec.Options
	exec.Options = nil

	if isCreate {
		exec.Id = 0
		exec.State = StateUnpublished
		if e := tx.Model(&Exercise{}).Create(&exec).Error; e != nil {
			return e
		}
	} else {
		exec.UpdatedTime = time.Now()
		if e := tx.Model(&Exercise{}).Where("id", exec.Id).Updates(&exec).Error; e != nil {
			return e
		}
	}

	if tx, e = exec.upgradeOption(tx, options); e != nil {
		return e
	}

	if e := tx.Model(&Exercise{}).Where("parent_id", exec.Id).Delete(&Exercise{}).Error; e != nil {
		return e
	}
	if len(subs) > 0 {
		for i, sub := range subs {
			sub.Sequence = i
			sub.ParentId = exec.Id
			// log.Infof("sub[%d]: %#v", i, sub)
			if e := sub.operate(tx, true); e != nil {
				return e
			}
		}
	}
	return nil
}

func (exec *Exercise) Create() error {
	return database.GetConnection().Transaction(func(tx *gorm.DB) (e error) {
		return exec.operate(tx, true)
	})
}

func (exec *Exercise) UpdateState(state state) error {
	return database.GetConnection().Model(&Exercise{}).Where("id", exec.Id).UpdateColumn("updated_time", time.Now()).UpdateColumn("state", state).Error
}

func (exec *Exercise) Update() error {
	return database.GetConnection().Transaction(func(tx *gorm.DB) (e error) {
		return exec.operate(tx, false)
	})
}

func (exec *Exercise) Delete() error {
	return exec.UpdateState(StateDeleted)
}

func (exec *Exercise) DeleteFromDisk() error {
	return database.GetConnection().Transaction(func(tx *gorm.DB) error {
		var ids []int
		if e := tx.Raw("SELECT id FROM exercise WHERE parent_id=?", exec.Id).Scan(&ids).Error; e != nil {
			return e
		}
		ids = append(ids, exec.Id)
		if e := tx.Model(&Exercise{}).Where("id in ?", ids).Delete(&Exercise{}).Error; e != nil {
			return e
		} else if e := tx.Model(&Option{}).Where("exercise_id in ?", ids).Delete(&Option{}).Error; e != nil {
			return e
		}
		return nil
	})
}

func Relation(tx *gorm.DB) *gorm.DB {
	tx = tx.Preload("Options")
	tx = tx.Preload("Subs").Preload("Subs.Options")
	return tx
}

func FindAll(states ...state) (execises []Exercise, e error) {
	tx := database.GetConnection().Model(&Exercise{})
	if len(states) == 0 {
		states = []state{StatePublished, StateUnpublished}
	}
	tx = tx.Where("parent_id=0").Where("state in ?", states)
	tx = Relation(tx)
	if e = tx.Find(&execises).Error; e != nil {
		return nil, e
	}
	return execises, nil
}
